Fix context_switch(). It is necessary to set_current() and
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Mon, 9 Jan 2006 11:25:05 +0000 (12:25 +0100)
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Mon, 9 Jan 2006 11:25:05 +0000 (12:25 +0100)
then check curr_vcpu all with interrupts disabled. This in
turn requires us to hoist the heck of next's vcpu_dirty_cpumask
as sending the flush IPI needs us to have interrupts enabled to
avoid deadlock.

Signed-off-by: Keir Fraser <keir@xensource.com>
xen/arch/x86/domain.c

index e44c79a1ddf4041d2347571e77a472649032461f..0d6517038f3e8150c167993659fc5980b6b2449d 100644 (file)
@@ -689,6 +689,9 @@ static void __context_switch(void)
     struct vcpu          *p = percpu_ctxt[cpu].curr_vcpu;
     struct vcpu          *n = current;
 
+    ASSERT(p != n);
+    ASSERT(cpus_empty(n->vcpu_dirty_cpumask));
+
     if ( !is_idle_domain(p->domain) )
     {
         memcpy(&p->arch.guest_context.user_regs,
@@ -748,24 +751,31 @@ static void __context_switch(void)
 void context_switch(struct vcpu *prev, struct vcpu *next)
 {
     unsigned int cpu = smp_processor_id();
+    cpumask_t dirty_mask = next->vcpu_dirty_cpumask;
 
     ASSERT(local_irq_is_enabled());
 
+    /* Allow at most one CPU at a time to be dirty. */
+    ASSERT(cpus_weight(dirty_mask) <= 1);
+    if ( unlikely(!cpu_isset(cpu, dirty_mask) && !cpus_empty(dirty_mask)) )
+    {
+        /* Other cpus call __sync_lazy_execstate from flush ipi handler. */
+        flush_tlb_mask(dirty_mask);
+    }
+
+    local_irq_disable();
+
     set_current(next);
 
-    if ( (percpu_ctxt[cpu].curr_vcpu != next) &&
-         !is_idle_domain(next->domain) )
+    if ( (percpu_ctxt[cpu].curr_vcpu == next) || is_idle_domain(next->domain) )
+    {
+        local_irq_enable();
+    }
+    else
     {
-        /* This may happen if next has been migrated by the scheduler. */
-        if ( unlikely(!cpus_empty(next->vcpu_dirty_cpumask)) )
-        {
-            ASSERT(!cpu_isset(cpu, next->vcpu_dirty_cpumask));
-            sync_vcpu_execstate(next);
-            ASSERT(cpus_empty(next->vcpu_dirty_cpumask));
-        }
-
-        local_irq_disable();
         __context_switch();
+
+        /* Re-enable interrupts before restoring state which may fault. */
         local_irq_enable();
 
         if ( VMX_DOMAIN(next) )